package com.fast.controller.completableFuture;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import cn.hutool.core.thread.ThreadUtil;
import com.baomidou.mybatisplus.extension.api.ApiController;
import com.baomidou.mybatisplus.extension.api.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author hujw
 * @since 2022/1/20
 */
@RestController
@Api(tags = "CompletableFuture的用法")
@RequestMapping("/completableFuture")
public class CompletableFutureController extends ApiController {

    @ApiOperation("张三去食堂吃饭-开启厨师做饭的异步任务")
    @GetMapping("test1")
    public R test1() {
        StringBuilder stringBuilder = new StringBuilder();
        String s1 = "张三进入餐厅,";
        System.out.println(Thread.currentThread().getName()+","+s1);
        stringBuilder.append(s1);
        String s2 = "张三点了1盘番茄炒蛋和1碗米饭,";
        System.out.println(Thread.currentThread().getName()+","+s2);
        stringBuilder.append(s2);
        //1.supplyAsync开启异步任务
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            String s3 = "厨师炒菜,";
            System.out.println(Thread.currentThread().getName()+","+s3);
            stringBuilder.append(s3);
            Assert.isTrue(ThreadUtil.sleep(200));
            String s4 = "厨师打饭,";
            System.out.println(Thread.currentThread().getName()+","+s4);
            stringBuilder.append(s4);
            Assert.isTrue(ThreadUtil.sleep(100));
            String s5 = "番茄炒蛋+米饭做好了";
            return s5;
        });
        String s6 = "张三在打王者,";
        System.out.println(Thread.currentThread().getName()+","+s6);
        stringBuilder.append(s6);
        String format = String.format("%s,张三开吃", completableFuture.join());
        System.out.println(Thread.currentThread().getName()+","+format);
        stringBuilder.append(format);
        return R.ok(stringBuilder.toString());
    }

    @ApiOperation("张三去食堂吃饭-厨师先干，干完服务员干")
    @GetMapping("test2")
    public R test2() {
        StringBuilder stringBuilder = new StringBuilder();
        String s1 = "张三进入餐厅,";
        System.out.println(Thread.currentThread().getName()+","+s1);
        stringBuilder.append(s1);
        String s2 = "张三点了1盘番茄炒蛋和1碗米饭,";
        System.out.println(Thread.currentThread().getName()+","+s2);
        stringBuilder.append(s2);
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            String s3 = "厨师炒菜,";
            System.out.println(Thread.currentThread().getName()+","+s3);
            stringBuilder.append(s3);
            Assert.isTrue(ThreadUtil.sleep(200));
            return "番茄炒蛋";

            //2.thenCompose：连接两个任务，等待上面任务完成后，把返回结果给下一个任务继续处理，处理完成后返回一个结果
        }).thenCompose(dish->CompletableFuture.supplyAsync(()->{
            String s4 = "服务员打饭,";
            System.out.println(Thread.currentThread().getName()+","+s4);
            stringBuilder.append(s4);
            Assert.isTrue(ThreadUtil.sleep(100));
            String s5 = dish+"+米饭做好了";
            return s5;
        }));
        String s6 = "张三在打王者,";
        System.out.println(Thread.currentThread().getName()+","+s6);
        stringBuilder.append(s6);
        String format = String.format("%s,张三开吃", completableFuture.join());
        System.out.println(Thread.currentThread().getName()+","+format);
        stringBuilder.append(format);
        return R.ok(stringBuilder.toString());
    }

    @ApiOperation("张三去食堂吃饭-厨师+服务员-同时干，干完合并到一起再返回")
    @GetMapping("test3")
    public R test3() {
        String s1 = "张三进入餐厅,";
        System.out.println(Thread.currentThread().getName()+","+s1);
        String s2 = "张三点了1盘番茄炒蛋和1碗米饭,";
        System.out.println(Thread.currentThread().getName()+","+s2);
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            String s3 = "厨师炒菜,";
            System.out.println(Thread.currentThread().getName()+","+s3);
            Assert.isTrue(ThreadUtil.sleep(200));
            return "番茄炒蛋";

            //3.thenCombine：合并两个任务，等待两个任务都完成后，把返回结果放在一起处理，处理完成后返回一个结果
        }).thenCombine(CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName()+","+"服务员蒸饭,");
            Assert.isTrue(ThreadUtil.sleep(300));
            return "+米饭做好了";
        }),(dish,rice)->{
            System.out.println("服务员打饭");
            Assert.isTrue(ThreadUtil.sleep(100));
            return String.format("%s+%s 好了", dish, rice);
        });
        String s6 = "张三在打王者,";
        System.out.println(Thread.currentThread().getName()+","+s6);
        String format = String.format("%s,张三开吃", completableFuture.join());
        System.out.println(Thread.currentThread().getName()+","+format);
        return R.ok("执行成功");
    }

    @ApiOperation("张三吃完饭去结账，一个服务员先收款再开发票")
    @GetMapping("test4")
    public R test4() {
        String s1 = "张三吃好了";
        System.out.println(Thread.currentThread().getName()+","+s1);
        String s2 = "张三要结账，要求开发票";
        System.out.println(Thread.currentThread().getName()+","+s2);
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            //----------------------------------------------------------
            String s3 = "服务员收款500元";
            System.out.println(Thread.currentThread().getName()+","+s3);
            Assert.isTrue(ThreadUtil.sleep(100));
            //----------------------------------------------------------

            //----------------------------------------------------------
            String s4 = "服务员开发票500元";
            System.out.println(Thread.currentThread().getName()+","+s4);
            Assert.isTrue(ThreadUtil.sleep(200));
            //----------------------------------------------------------
            return "500元发票";
        });
        String s6 = "张三接到朋友的电话，想一起打王者,";
        System.out.println(Thread.currentThread().getName()+","+s6);
        String format = String.format("张三拿到%s,准备回家", completableFuture.join());
        System.out.println(Thread.currentThread().getName()+","+format);
        return R.ok("执行成功");
    }

    @ApiOperation("张三吃完饭去结账，1个服务员先收款再开发票升级版")
    @GetMapping("test5")
    public R test5() {
        String s1 = "张三吃好了";
        System.out.println(Thread.currentThread().getName()+","+s1);
        String s2 = "张三要结账，要求开发票";
        System.out.println(Thread.currentThread().getName()+","+s2);
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            //----------------------------------------------------------
            String s3 = "服务员收款500元";
            System.out.println(Thread.currentThread().getName()+","+s3);
            Assert.isTrue(ThreadUtil.sleep(100));
            //----------------------------------------------------------
            return "500";
            //thenApply:把前面任务的执行结果，交给后面的Function，处理完后返回一个结果
        }).thenApply(invoice->{
            //----------------------------------------------------------
            String s4 = String.format("服务员开发票%s元",invoice);
            System.out.println(Thread.currentThread().getName()+","+s4);
            Assert.isTrue(ThreadUtil.sleep(200));
            //----------------------------------------------------------
            return invoice+"元发票";
        });
        String s6 = "张三接到朋友的电话，想一起打王者,";
        System.out.println(Thread.currentThread().getName()+","+s6);
        String format = String.format("张三拿到%s,准备回家", completableFuture.join());
        System.out.println(Thread.currentThread().getName()+","+format);
        return R.ok("执行成功");
    }

    @ApiOperation("张三吃完饭去结账，2个服务员收款同时开发票")
    @GetMapping("test6")
    public R test6() {
        String s1 = "张三吃好了";
        System.out.println(Thread.currentThread().getName()+","+s1);
        String s2 = "张三要结账，要求开发票";
        System.out.println(Thread.currentThread().getName()+","+s2);
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            //----------------------------------------------------------
            String s3 = "服务员收款500元";
            System.out.println(Thread.currentThread().getName()+","+s3);
            Assert.isTrue(ThreadUtil.sleep(100));
            //----------------------------------------------------------
            return "500";
            //thenApplyAsync:把前面任务的执行结果，交给后面的Function，后面开启一个新的线程处理，处理完后返回一个结果
            //类似thenCompose
        }).thenApplyAsync(invoice->{
            //----------------------------------------------------------
            String s4 = String.format("服务员开发票%s元",invoice);
            System.out.println(Thread.currentThread().getName()+","+s4);
            Assert.isTrue(ThreadUtil.sleep(200));
            //----------------------------------------------------------
            return invoice+"元发票";
        });
        String s6 = "张三接到朋友的电话，想一起打王者,";
        System.out.println(Thread.currentThread().getName()+","+s6);
        String format = String.format("张三拿到%s,准备回家", completableFuture.join());
        System.out.println(Thread.currentThread().getName()+","+format);
        return R.ok("执行成功");
    }

    @ApiOperation("张三结完账去等公交，2个公交车谁先来坐谁")
    @GetMapping("test7")
    public R test7() {
        String s1 = "张三走出餐厅，来到公交站";
        System.out.println(Thread.currentThread().getName()+","+s1);
        String s2 = "等待700路或者800路公交车";
        System.out.println(Thread.currentThread().getName()+","+s2);
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            //----------------------------------------------------------
            String s3 = "700路公交车正在赶来";
            System.out.println(Thread.currentThread().getName()+","+s3);
            Assert.isTrue(ThreadUtil.sleep(200));
            //----------------------------------------------------------
            return "700路到了";
            //applyToEither:上个任务和这个任务一起执行，谁先运行完，就用谁的返回结果交给Function继续新的任务
        }).applyToEither(CompletableFuture.supplyAsync(() ->{
            //----------------------------------------------------------
            String s4 = String.format("800路公交车正在赶来");
            System.out.println(Thread.currentThread().getName()+","+s4);
            Assert.isTrue(ThreadUtil.sleep(100));
            //----------------------------------------------------------
            return "800路公交车到了";
        }),firstComeBus->firstComeBus);
        String format = String.format("%s,张三乘车回家", completableFuture.join());
        System.out.println(Thread.currentThread().getName()+","+format);
        return R.ok("执行成功");
    }

    @ApiOperation("张三结完账去等公交，2个公交车谁先来坐谁")
    @GetMapping("test8")
    public R test8() {
        String s1 = "张三走出餐厅，来到公交站";
        System.out.println(Thread.currentThread().getName()+","+s1);
        String s2 = "等待700路或者800路公交车";
        System.out.println(Thread.currentThread().getName()+","+s2);
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            //----------------------------------------------------------
            String s3 = "700路公交车正在赶来";
            System.out.println(Thread.currentThread().getName()+","+s3);
            Assert.isTrue(ThreadUtil.sleep(200));
            //----------------------------------------------------------
            return "700路公交车到了";
            //applyToEither:上个任务和这个任务一起执行，谁先运行完，就用谁的返回结果交给Function继续新的任务
        }).applyToEither(CompletableFuture.supplyAsync(() ->{
            //----------------------------------------------------------
            String s4 = String.format("800路公交车正在赶来");
            System.out.println(Thread.currentThread().getName()+","+s4);
            Assert.isTrue(ThreadUtil.sleep(400));
            //----------------------------------------------------------
            return "800路公交车到了";
        }),firstComeBus-> {
            System.out.println(firstComeBus);
            if (firstComeBus.startsWith("700")){
                throw new RuntimeException("撞树了。。。。");
            }
            return firstComeBus;
        }).exceptionally(e->{
            System.out.println(e.getMessage());
            System.out.println("张三叫出租车");
            return "出租车到了";
        });
        String format = String.format("%s,张三乘车回家", completableFuture.join());
        System.out.println(Thread.currentThread().getName()+","+format);
        return R.ok("执行成功");
    }

    @ApiOperation("张三-错误示范，这个会顺序执行")
    @GetMapping("test9")
    public R test9() {
        String s1 = "张三和小伙伴们进餐厅点餐";
        System.out.println(Thread.currentThread().getName()+","+s1);
        List<Dish> list = new ArrayList<>();

        //点菜
        for (int i = 1; i < 10; i++) {
            Dish dish = new Dish("菜" + i, 1000);
            list.add(dish);
        }

        //做菜
        for (Dish dish : list) {
            CompletableFuture.runAsync(()->dish.make()).join();
        }

        System.out.println("菜都做好了，上桌");

        return R.ok("执行成功");
    }

    @ApiOperation("张三-正确用法线程池异步处理")
    @GetMapping("test10")
    public R test10(Integer fen) {
        String s1 = "张三和小伙伴们进餐厅点餐";
        System.out.println(Thread.currentThread().getName()+","+s1);
        int cpu = Runtime.getRuntime().availableProcessors();
        System.out.println("电脑核心数:"+ cpu);
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(cpu*2,
                cpu*2,
                60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(300),
                ThreadFactoryBuilder.create().setNamePrefix("splitOpenInv_thread_pool_").build(),
                new ThreadPoolExecutor.AbortPolicy());

        //点菜
        List<Dish> list = new ArrayList<>();
        for (int i = 1; i < fen; i++) {
            Dish dish = new Dish("菜" + i, 1000);
            list.add(dish);
        }

        //做菜
        List<CompletableFuture> cfList = new ArrayList<>();
        for (Dish dish : list) {
            CompletableFuture<Void> cf = CompletableFuture.runAsync(() -> dish.make(),threadPoolExecutor);
            cfList.add(cf);
        }
        CompletableFuture.allOf(cfList.toArray(new CompletableFuture[cfList.size()])).join();
        threadPoolExecutor.shutdown();
        System.out.println("菜都做好了，上桌");

        return R.ok("执行成功");
    }

}
